package com.wcs.exam.controller.api.biz;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
import com.wcs.exam.common.base.BaseBiz;
import com.wcs.exam.common.base.Result;
import com.wcs.exam.common.base.page.Page;
import com.wcs.exam.common.base.page.PageUtil;
import com.wcs.exam.common.base.page.SqlUtil;
import com.wcs.exam.common.enums.ExamTypeEnum;
import com.wcs.exam.common.enums.PublishStatusEnum;
import com.wcs.exam.common.enums.StatusEnum;
import com.wcs.exam.common.util.BeanUtil;
import com.wcs.exam.controller.api.req.ApiExamPageReq;
import com.wcs.exam.controller.api.req.ApiExamRankPageReq;
import com.wcs.exam.controller.api.resp.ApiExamPageResp;
import com.wcs.exam.controller.api.resp.ApiExamRankPageResp;
import com.wcs.exam.controller.api.resp.ApiExamViewResp;
import com.wcs.exam.controller.biz.CategoryCommonBiz;
import com.wcs.exam.dao.ExamDao;
import com.wcs.exam.dao.ExamUserDao;
import com.wcs.exam.dao.PaperDao;
import com.wcs.exam.dao.UsersDao;
import com.wcs.exam.dao.impl.mapper.entity.Exam;
import com.wcs.exam.dao.impl.mapper.entity.ExamUser;
import com.wcs.exam.dao.impl.mapper.entity.Paper;
import com.wcs.exam.dao.impl.mapper.entity.Users;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Api-考试
 *
 * @author wcs
 */
@Component
@RequiredArgsConstructor
public class ApiExamBiz extends BaseBiz {

    @NotNull
    private final CategoryCommonBiz categoryCommonBiz;

    @NotNull
    private final ExamDao dao;
    @NotNull
    private final PaperDao paperDao;
    @NotNull
    private final ExamUserDao examUserDao;
    @NotNull
    private final UsersDao usersDao;


    /**
     * 考试-分页
     *
     * @param req 分页查询参数
     * @return 考试
     */
    public Result<Page<ApiExamPageResp>> search(ApiExamPageReq req) {
        // 分类ID
        Set<Long> categoryIds = new HashSet<>();
        if (ObjectUtil.isNotNull(req.getCategoryId())) {
            categoryIds.add(req.getCategoryId());
            List<Long> childCategoryIds = categoryCommonBiz.listChildrenIdById(req.getCategoryId());
            if (CollectionUtil.isNotEmpty(childCategoryIds)) {
                categoryIds.addAll(childCategoryIds);
            }
        }

        // 搜索条件
        LambdaQueryWrapper<Exam> wrapper = new LambdaQueryWrapper<Exam>()
                .eq(ObjectUtil.isNotNull(req.getExamType()), Exam::getExamType, req.getExamType())
                .like(StrUtil.isNotBlank(req.getExamName()), Exam::getExamName, req.getExamName())
                .in(CollectionUtil.isNotEmpty(categoryIds), Exam::getCategoryId, categoryIds)
                .like(StrUtil.isNotBlank(req.getDescription()), Exam::getDescription, req.getDescription())
                .eq(Exam::getPublishStatus, PublishStatusEnum.PUBLISHED.getCode())
                .eq(Exam::getStatus, StatusEnum.NORMAL.getCode());

        // 收费情况
        if (ObjectUtil.isNotNull(req.getFree())) {
            if (Boolean.TRUE.equals(req.getFree())) {
                wrapper.eq(Exam::getPrice, BigDecimal.ZERO);
            } else {
                wrapper.ne(Exam::getPrice, BigDecimal.ZERO);
            }
        }

        // 处理报名状态
        if (ExamTypeEnum.EXAMINATION.getCode().equals(req.getExamType()) && ObjectUtil.isNotNull(req.getRegistrationStatus())) {
            if (req.getRegistrationStatus() == 1) {
                // 未开始
                wrapper.gt(Exam::getRegistrationStartTime, LocalDateTime.now());
            } else if (req.getRegistrationStatus() == 2) {
                // 报名中
                wrapper.le(Exam::getRegistrationStartTime, LocalDateTime.now())
                        .ge(Exam::getRegistrationEndTime, LocalDateTime.now());
            } else if (req.getRegistrationStatus() == 3) {
                // 已结束
                wrapper.lt(Exam::getRegistrationEndTime, LocalDateTime.now());
            }
        }

        // 排序
        wrapper.orderByDesc(Exam::getSort, Exam::getId);

        IPage<Exam> page = dao.page(PageDTO.of(req.getPageCurrent(), req.getPageSize()), wrapper);
        if (ObjectUtil.isNull(page) || CollectionUtil.isEmpty(page.getRecords())) {
            return Result.success(new Page<>());
        }
        Page<ApiExamPageResp> respPage = PageUtil.transform(page, ApiExamPageResp.class);

        // 处理分页结果
        for (ApiExamPageResp resp : respPage.getList()) {
            // 处理报名状态
            if (ExamTypeEnum.EXAMINATION.getCode().equals(resp.getExamType())) {
                if (LocalDateTime.now().isBefore(resp.getRegistrationStartTime())) {
                    resp.setRegistrationStatus(1);
                } else if (!LocalDateTime.now().isBefore(resp.getRegistrationStartTime()) && !LocalDateTime.now().isAfter(resp.getRegistrationEndTime())) {
                    resp.setRegistrationStatus(2);
                } else {
                    resp.setRegistrationStatus(3);
                }
            } else {
                resp.setRegistrationStatus(1);
            }
        }
        return Result.success(respPage);
    }

    /**
     * 考试-查看
     *
     * @param id 考试ID
     * @return 考试
     */
    public Result<ApiExamViewResp> view(Long id) {
        Exam exam = dao.getById(id);
        if (ObjectUtil.isNull(exam)) {
            return Result.error(toI18n("resource.exam.not.exist"));
        }
        ApiExamViewResp resp = BeanUtil.copyProperties(exam, ApiExamViewResp.class);

        Paper paper = paperDao.getById(resp.getPaperId());
        if (ObjectUtil.isNotNull(paper)) {
            resp.setTotalScore(paper.getTotalScore());
            resp.setQuestionCount(paper.getQuestionCount());
        }

        // 处理报名状态
        if (ExamTypeEnum.EXAMINATION.getCode().equals(resp.getExamType())) {
            if (LocalDateTime.now().isBefore(resp.getRegistrationStartTime())) {
                resp.setRegistrationStatus(1);
            } else if (!LocalDateTime.now().isBefore(resp.getRegistrationStartTime()) && !LocalDateTime.now().isAfter(resp.getRegistrationEndTime())) {
                resp.setRegistrationStatus(2);
            } else {
                resp.setRegistrationStatus(3);
            }
        } else {
            resp.setRegistrationStatus(1);
        }
        return Result.success(resp);
    }

    /**
     * 考试-考试排行榜分页
     *
     * @param req 排名参数
     * @return 排名结果
     */
    public Result<Page<ApiExamRankPageResp>> rankPage(ApiExamRankPageReq req) {
        if (ExamTypeEnum.MOCK.getCode().equals(req.getExamType())) {
            // 模拟考试
            return examRankPage(req);
        } else if (ExamTypeEnum.EXAMINATION.getCode().equals(req.getExamType())) {
            // 正式考试
            return examRankPage(req);
        } else {
            // 其他类型
            return Result.error("该考试类型暂不支持");
        }
    }

    /**
     * 考试-考试排行榜分页
     *
     * @param req 排名参数
     * @return 排名结果
     */
    private Result<Page<ApiExamRankPageResp>> examRankPage(ApiExamRankPageReq req) {
        // 查询总条数
        Long totalCount = examUserDao.lambdaQuery().eq(ExamUser::getExamId, req.getRelateId()).count();
        if (ObjectUtil.isNull(totalCount) || totalCount <= 0L) {
            return Result.success(new Page<>());
        }

        // 校验当前页
        long pageCurrent = PageUtil.checkCurrent(totalCount, req.getPageSize(), req.getPageCurrent());
        if (pageCurrent != req.getPageCurrent()) {
            return Result.success(new Page<>());
        }

        // 计算总页数
        long totalPage = PageUtil.countTotalSize(totalCount, req.getPageSize());

        // 处理查询条件
        LambdaQueryWrapper<ExamUser> wrapper = new QueryWrapper<ExamUser>().select("user_id as userId", "exam_id as examId", "exam_type as examType", "user_score as userScore", "exam_submit_time as examSubmitTime", "user_answer_time as userAnswerTime", "user_exam_status as userExamStatus", "user_evaluation_status as userEvaluationStatus", "RANK() OVER (ORDER BY user_score DESC) AS rankNo").orderByAsc("rankNo").lambda().eq(ExamUser::getExamType, req.getExamType()).eq(ExamUser::getExamId, req.getRelateId()).last(SqlUtil.limitSql(totalCount, pageCurrent, req.getPageSize()));

        // 查询排名信息
        List<ApiExamRankPageResp> respList = examUserDao.selectObjs(wrapper, ApiExamRankPageResp.class);
        if (CollectionUtil.isEmpty(respList)) {
            return Result.success(new Page<>());
        }

        // 查询用户信息
        Set<Long> userIds = respList.stream().map(ApiExamRankPageResp::getUserId).collect(Collectors.toSet());
        List<Users> usersList = usersDao.listByIds(userIds);
        Map<Long, Users> userMap = new HashMap<>();
        if (CollectionUtil.isEmpty(usersList)) {
            usersList.forEach(item -> userMap.put(item.getId(), item));
        }

        // 处理排名结果
        for (ApiExamRankPageResp resp : respList) {
            if (CollectionUtil.isNotEmpty(userMap)) {
                Users user = userMap.get(resp.getUserId());
                if (ObjectUtil.isNotNull(user)) {
                    resp.setNickname(user.getNickname());
                    resp.setUserHead(user.getUserHead());
                }
            }
        }

        // 处理响应信息
        Page<ApiExamRankPageResp> respPage = new Page<>(respList, totalCount.intValue(), (int) totalPage, req.getPageCurrent(), req.getPageSize());
        return Result.success(respPage);
    }
}
